<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>TUN324</title>
  <link rel="icon" href="imgs/logo.png" />
  <script src="css/tailwindcss.css"></script>
  <link href="css/font-awesome.min.css" rel="stylesheet">
  
  <!-- 优先加载Three.js核心库，并添加加载失败处理 -->
  <script src="js/three.min.js"></script>
  
  <!-- 其他Three.js组件 -->
  <script src="js/controls/OrbitControls.min.js"></script>
  <script src="js/renderers/CSS2DRenderer.min.js"></script>
  
  <script>
    tailwind.config = {
      theme: {
        extend: {
          colors: {
            primary: '#3b82f6',
            secondary: '#6366f1',
            success: '#10b981',
            warning: '#f59e0b',
            danger: '#ef4444',
          }
        }
      }
    }
  </script>
  
  <style type="text/tailwindcss">
    @layer utilities {
      .step-btn {
        width: 32px;
        height: 32px;
        border-radius: 50%;
        background-color: #f1f5f9;
        color: #64748b;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 14px;
        font-weight: 500;
        transition: all 0.2s ease;
        border: none;
        cursor: pointer;
        box-shadow: 0 1px 2px rgba(0,0,0,0.05);
      }
      .step-btn:hover:not(.step-active) {
        background-color: #e2e8f0;
        transform: translateY(-1px);
        box-shadow: 0 2px 4px rgba(0,0,0,0.08);
      }
      .step-active {
        background-color: #3b82f6;
        color: white;
        box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.3), 0 2px 4px rgba(59, 130, 246, 0.2);
      }
    }
  </style>
</head>
<body class="bg-gray-50 min-h-screen overflow-hidden">
  <!-- 顶部导航浮层 -->
  <div class="absolute top-3 left-1/2 transform -translate-x-1/2 bg-white/90 backdrop-blur-md shadow-md rounded-lg p-3 z-50 flex flex-col md:flex-row items-center justify-between w-[80vw] max-w-5xl">
    <!-- Logo和标题 -->
    <div class="flex items-center mb-2 md:mb-0">
      <img src="imgs/logo.png" alt="Logo" class="h-8 w-auto mr-3">
      <span class="font-bold text-lg text-gray-800">TUN324</span>
      <span class="text-gray-500">&nbsp;- TUN/TAP设备三层IP重定向转四层Socket演示动画</span>
    </div>
    
    <!-- 链接区域 -->
    <div class="flex space-x-6">
      <a href="https://github.com/snltty/tun324" target="_blank" class="flex items-center text-gray-600 hover:text-primary transition-colors">
        <i class="fa fa-github mr-1"></i>
        <span>Github</span>
      </a>
      <a href="https://linker.snltty.com" target="_blank" class="flex items-center text-gray-600 hover:text-primary transition-colors">
        <i class="fa fa-link mr-1"></i>
        <span>Linker</span>
      </a>
      <a href="https://blog.snltty.com" target="_blank" class="flex items-center text-gray-600 hover:text-primary transition-colors">
        <i class="fa fa-book mr-1"></i>
        <span>Blog</span>
      </a>
    </div>
  </div>

  <!-- Three.js容器 -->
  <div id="canvasContainer" class="w-full h-screen relative"></div>
  
  <!-- 底部控制区 -->
  <div class="absolute bottom-3 left-1/2 transform -translate-x-1/2 bg-white/90 backdrop-blur-md shadow-md rounded-lg p-2 z-40 flex flex-col items-center">
    <div id="stepButtons" class="flex justify-center space-x-2 px-1 overflow-x-auto max-w-[80vw]"></div>
  </div>
  
  <!-- 右侧信息面板 -->
  <div class="absolute bottom-24 right-4 bg-white/90 backdrop-blur-md shadow-lg rounded-xl p-4 z-40 w-96 max-h-[calc(100vh-120px)] flex flex-col">
    <!-- 数据包信息 -->
    <div class="mb-4">
      <div id="noPacketMessage" class="text-sm text-gray-500 italic">
        点击下方步骤按钮播放动画
      </div>
      <div id="packetInfoContent" class="hidden">
        <div class="flex justify-between items-start mb-2">
          <span id="infoPacketType" class="font-bold text-xs px-2 py-1 rounded"></span>
        </div>
        <div class="space-y-1 text-xs">
          <div class="flex justify-between">
            <span class="text-gray-500">源:</span>
            <span id="infoSrc" class="font-mono"></span>
          </div>
          <div class="flex justify-between">
            <span class="text-gray-500">目的:</span>
            <span id="infoDst" class="font-mono"></span>
          </div>
          <div class="mt-2 text-gray-600">
            <span id="infoDescription"></span>
          </div>
        </div>
      </div>
    </div>
    
    <!-- 映射表 -->
    <div class="flex-grow overflow-y-auto">
      <div class="overflow-x-auto">
        <table id="mappingTable" class="min-w-full text-xs">
          <thead>
            <tr class="bg-gray-50">
              <th class="px-2 py-1 text-left">Key (源IP:端口)</th>
              <th class="px-2 py-1 text-left">目标IP:端口</th>
              <th class="px-2 py-1 text-left">源端口</th>
              <th class="px-2 py-1 text-left">随机端口</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td colspan="4" class="px-2 py-1 text-center text-gray-500">暂无映射数据</td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
  </div>
</body>
<script>
    // 确保Three.js加载完成后再执行代码
    function waitForThreeJS() {
      if (window.THREE) {
        initApp();
      } else {
        // 每100ms检查一次Three.js是否加载完成
        setTimeout(waitForThreeJS, 100);
      }
    }

    window.addEventListener('load', function() {
      // 开始等待Three.js加载
      waitForThreeJS();
    });
    
    function initApp() {
      // 动画步骤数据
      const animationSteps = [
        {"From":"浏览器",To:"虚拟网卡","Packet":"10.18.18.2:11111->10.18.18.3:80 SYN","Remark":"浏览器访问10.18.18.3:80，产生10.18.18.2:11111->10.18.18.3:80 SYN"},
        {"From":"虚拟网卡",To:"处理程序","Packet":"10.18.18.2:11111->10.18.18.3:80 SYN","Remark":"处理程序读取到数据包，申请随机端口得到22222，添加映射关系(10.18.18.2,11111)->(10.18.18.3,80,11111,22222)，(10.18.18.2,22222)->(10.18.18.3,80,11111,22222)"},
        {"From":"处理程序",To:"虚拟网卡","Packet":"10.18.18.0:22222->10.18.18.2:80 SYN","Remark":"修改包为10.18.18.0:22222->10.18.18.2:80 SYN，重新写入网卡"},
        {"From":"虚拟网卡",To:"目标服务","Packet":"10.18.18.0:22222->10.18.18.2:80 SYN","Remark":"数据包10.18.18.0:22222->10.18.18.2:80 SYN流向目标服务"},
        {"From":"目标服务",To:"虚拟网卡","Packet":"10.18.18.2:80->10.18.18.0:22222 SYN+ACK","Remark":"目标服务收到SYN，回复SYN+ACK，产生10.18.18.2:80->10.18.18.0:22222 SYN+ACK"},
        {"From":"虚拟网卡",To:"处理程序","Packet":"10.18.18.2:80->10.18.18.0:22222 SYN+ACK","Remark":"处理程序读取数据包，根据Key(10.18.18.2,22222)查找映射值(10.18.18.3,80,11111,22222)"},
        {"From":"处理程序",To:"虚拟网卡","Packet":"10.18.18.3:80->10.18.18.2:11111 SYN+ACK","Remark":"修改包为10.18.18.3:80->10.18.18.2:11111，重新写入网卡"},
        {"From":"虚拟网卡",To:"浏览器","Packet":"10.18.18.3:80->10.18.18.2:11111 SYN+ACK","Remark":"数据包10.18.18.3:80->10.18.18.2:11111流向浏览器"},
        {"From":"浏览器",To:"虚拟网卡","Packet":"10.18.18.2:11111->10.18.18.3:80 ACK","Remark":"浏览器收到SYN+ACK，回复ACK,产生10.18.18.2:11111->10.18.18.3:80 ACK"},
        {"From":"虚拟网卡",To:"处理程序","Packet":"10.18.18.2:11111->10.18.18.3:80 ACK","Remark":"处理程序读取数据包，根据Key(10.18.18.2,11111)查找映射值(10.18.18.3,80,11111,22222)"},
        {"From":"处理程序",To:"虚拟网卡","Packet":"10.18.18.0:22222->10.18.18.2:80 ACK","Remark":"修改包为10.18.18.0:22222->10.18.18.2:80，重新写入网卡"},
        {"From":"虚拟网卡",To:"目标服务","Packet":"10.18.18.0:22222->10.18.18.2:80 ACK","Remark":"数据包10.18.18.0:22222->10.18.18.2:80流向目标服务"}
      ];

      // Three.js相关变量
      let scene, camera, renderer, controls, labelRenderer;
      let nodes = {};
      let connections = [];
      let currentPacket = null;
      let animationId = null;
      let font = null;
      
      // 状态变量
      let mappingTable = [];
      let currentStep = -1;
      let isAnimating = false;
      let animationSpeed = 1.5;
      let packetPosition = 0;
      let currentAnimationStep = null;

      // DOM元素
      const canvasContainer = document.getElementById('canvasContainer');
      const stepButtons = document.getElementById('stepButtons');
      const mappingTableEl = document.getElementById('mappingTable').querySelector('tbody');
      const closeModal = document.getElementById('closeModal');
      const closeModalBtn = document.getElementById('closeModalBtn');
      
      // 初始化Three.js场景
      function initThreeJS() {
        try {
          // 创建场景
          scene = new THREE.Scene();
          scene.background = new THREE.Color(0xf8fafc);
          
          // 创建相机
          camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
          camera.position.z = 200;
          camera.position.x = 400;
          camera.position.y = 300;
          camera.lookAt(new THREE.Vector3(0, 0, 0));
          
          // 创建渲染器
          renderer = new THREE.WebGLRenderer({ antialias: true });
          renderer.setSize(window.innerWidth, window.innerHeight);
          canvasContainer.appendChild(renderer.domElement);
          
          // 初始化控制器
          if (typeof THREE.OrbitControls !== 'undefined') {
            controls = new THREE.OrbitControls(camera, renderer.domElement);
            controls.enableDamping = true;
            controls.dampingFactor = 0.1;
            controls.rotateSpeed = 0.5;
            controls.zoomSpeed = 0.7;
          } else {
            // 如果没有OrbitControls，使用基本的旋转控制
            let isRotating = false;
            let previousMousePosition = { x: 0, y: 0 };
            
            renderer.domElement.addEventListener('mousedown', (e) => {
              isRotating = true;
              previousMousePosition = {
                x: e.clientX,
                y: e.clientY
              };
            });
            
            window.addEventListener('mousemove', (e) => {
              if (!isRotating) return;
              
              const deltaMove = {
                x: e.clientX - previousMousePosition.x,
                y: e.clientY - previousMousePosition.y
              };
              
              scene.rotation.y += deltaMove.x * 0.005;
              scene.rotation.x += deltaMove.y * 0.005;
              
              previousMousePosition = {
                x: e.clientX,
                y: e.clientY
              };
            });
            
            window.addEventListener('mouseup', () => {
              isRotating = false;
            });
          }
          
          // 初始化CSS2D渲染器
          if (typeof THREE.CSS2DRenderer !== 'undefined') {
            labelRenderer = new THREE.CSS2DRenderer();
            labelRenderer.setSize(window.innerWidth, window.innerHeight);
            labelRenderer.domElement.style.position = 'absolute';
            labelRenderer.domElement.style.top = '0';
            labelRenderer.domElement.style.pointerEvents = 'none';
            canvasContainer.appendChild(labelRenderer.domElement);
          }
          
          // 添加环境光
          const ambientLight = new THREE.AmbientLight(0xffffff, 0.7);
          scene.add(ambientLight);
          
          // 添加方向光
          const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
          directionalLight.position.set(50, 100, 75);
          scene.add(directionalLight);
          
          // 窗口大小调整
          window.addEventListener('resize', onWindowResize);
          
          // 加载字体然后创建节点
          loadFont().then(() => {
            createNodes();
            createConnections();
            animate();
          }).catch(error => {
            console.error(`字体加载失败: ${error.message}`);
            // 即使字体加载失败也继续创建节点
            createNodes();
            createConnections();
            animate();
          });
          
        } catch (error) {
          console.error(`Three.js初始化失败: ${error.message}`);
        }
      }
      
      // 加载字体用于3D文本
      function loadFont() {
        return new Promise((resolve, reject) => {
          const fontLoader = new THREE.FontLoader();
          fontLoader.load(
            'fonts/helvetiker_regular.typeface.json', 
            (loadedFont) => {
              font = loadedFont;
              resolve();
            },
            null,
            (error) => {
              reject(new Error(`字体加载错误: ${error.message}`));
            }
          );
        });
      }
      
      // 窗口大小调整
      function onWindowResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
        if (labelRenderer) {
          labelRenderer.setSize(window.innerWidth, window.innerHeight);
        }
      }
      
      // 创建节点 - 所有面使用同一张纹理并添加背景色
      function createNodes() {
        // 节点配置
        const nodeConfigs = {
          "浏览器": { 
            x: -200, y: 0, z: 150, 
            color: 0x2563eb,         // 背景色
            bgOpacity: 0.9,          // 背景透明度
            shape: 'box',
            texture: 'imgs/chrome.svg',
            size: 45, 
            glow: true,
            spinning: true
          },
          "虚拟网卡": { 
            x: -200, y: 0, z: 0, 
            color: 0x7c3aed, 
            bgOpacity: 0.9,
            shape: 'box',
            texture: 'imgs/wintun.svg',
            size: 42, 
            rings: true,
            spinning: true
          },
          "目标服务": { 
            x: -200, y: 0, z: -150, 
            color: 0x059669, 
            bgOpacity: 0.9,
            shape: 'box',
            texture: 'imgs/service.svg',
            size: 48,
            spinning: true
          },
          "处理程序": { 
            x: 100, y: 0, z: 0, 
            color: 0xd97706, 
            bgOpacity: 0.9,
            shape: 'box',
            texture: 'imgs/logo.png',
            size: 50, 
            spinning: true
          }
        };
        
        Object.keys(nodeConfigs).forEach(name => {
          try {
            const config = nodeConfigs[name];
            // 使用正方体几何体
            const geometry = new THREE.BoxGeometry(config.size, config.size, config.size);
            
            // 先创建背景材质
            const bgMaterial = new THREE.MeshStandardMaterial({
              color: config.color,
              metalness: 0.8,
              roughness: 0.2,
              opacity: config.bgOpacity,
              transparent: true
            });
            
            // 创建一个稍微小一点的内部立方体作为背景
            const innerGeometry = new THREE.BoxGeometry(
              config.size * 0.99,  // 稍微小一点，避免与外层重叠
              config.size * 0.99, 
              config.size * 0.99
            );
            const innerCube = new THREE.Mesh(innerGeometry, bgMaterial);
            
            // 加载纹理 - 所有面共用同一张图
            const textureLoader = new THREE.TextureLoader();
            let outerMaterial = new THREE.MeshStandardMaterial({
              color: config.color,
              metalness: 0.3,
              roughness: 0.7,
              transparent: true,
              opacity: 1
            });
            
            // 加载纹理并应用到所有面
            textureLoader.load(
              config.texture,
              (texture) => {
                // 创建外层材质（带透明纹理）
                outerMaterial = new THREE.MeshStandardMaterial({
                  map: texture,
                  transparent: true,
                  side: THREE.FrontSide,
                  metalness: 0.3,
                  roughness: 0.7,
                  opacity: 1
                });
                outerCube.material = outerMaterial;
              },
              undefined,
              (error) => {
                console.error(`加载纹理失败 ${config.texture}: ${error.message}`);
                // 加载失败时保持默认材质
              }
            );
            
            // 创建外层立方体（带纹理）
            const outerCube = new THREE.Mesh(geometry, outerMaterial);
            
            // 创建组合对象，包含内层背景和外层纹理
            const nodeGroup = new THREE.Group();
            nodeGroup.add(innerCube);  // 先添加背景
            nodeGroup.add(outerCube);  // 再添加纹理层
            nodeGroup.position.set(config.x, config.y, config.z);
            
            // 设置旋转属性
            nodeGroup.userData.rotationSpeed = config.spinning ? 0.02 : (Math.random() * 0.01 + 0.005);
            nodeGroup.userData.rotationAxis = 'y'; // 沿Y轴旋转
            
            // 添加边框
            const edgeGeometry = new THREE.EdgesGeometry(geometry);
            const edgeMaterial = new THREE.LineBasicMaterial({ color: 0xffffff, linewidth: 2 });
            const edge = new THREE.LineSegments(edgeGeometry, edgeMaterial);
            nodeGroup.add(edge);
            
            // 创建标签
            if (labelRenderer) {
              const div = document.createElement('div');
              div.className = 'px-3 py-1.5 bg-black/70 text-white text-sm rounded-md shadow-md whitespace-nowrap';
              div.textContent = name;
              const label = new THREE.CSS2DObject(div);
              label.position.set(0, -config.size - 15, 0); // 相对于组的位置
              nodeGroup.add(label);
            }
            
            // 添加到场景
            scene.add(nodeGroup);
            
            // 添加特效
            if (config.glow) {
              const glowGeometry = new THREE.SphereGeometry(config.size/2 + 10, 32, 32);
              const glowMaterial = new THREE.MeshBasicMaterial({
                color: config.color,
                transparent: true,
                opacity: 0.3,
                side: THREE.BackSide
              });
              const glow = new THREE.Mesh(glowGeometry, glowMaterial);
              nodeGroup.add(glow);
              nodeGroup.userData.glow = glow;
            }
            
            if (config.rings) {
              for (let i = 0; i < 2; i++) {
                const ringGeometry = new THREE.RingGeometry(
                  config.size/2 + 10 + i*10, 
                  config.size/2 + 12 + i*10, 
                  64
                );
                const ringMaterial = new THREE.MeshBasicMaterial({
                  color: config.color,
                  transparent: true,
                  opacity: 0.5,
                  side: THREE.DoubleSide
                });
                const ring = new THREE.Mesh(ringGeometry, ringMaterial);
                ring.rotation.x = Math.PI / 2;
                ring.userData.rotationSpeed = 0.01 + i*0.005;
                nodeGroup.add(ring);
                
                if (!nodeGroup.userData.rings) nodeGroup.userData.rings = [];
                nodeGroup.userData.rings.push(ring);
              }
            }
            
            nodes[name] = {
              mesh: nodeGroup,
              position: new THREE.Vector3(config.x, config.y, config.z)
            };
            
          } catch (error) {
            console.error(`节点创建失败 ${name}: ${error.message}`);
          }
        });
      }
      
      // 创建连接线
      function createConnections() {
        const connectionPairs = [
          ["浏览器", "虚拟网卡"],
          ["虚拟网卡", "目标服务"],
          ["虚拟网卡", "处理程序"]
        ];
        
        connectionPairs.forEach(pair => {
          try {
            const start = nodes[pair[0]].position;
            const end = nodes[pair[1]].position;
            
            // 创建主连接线
            const lineGeometry = new THREE.BufferGeometry().setFromPoints([start, end]);
            const lineMaterial = new THREE.LineBasicMaterial({
              color: 0x64748b,
              linewidth: 3,
              transparent: true,
              opacity: 0.7
            });
            
            const line = new THREE.Line(lineGeometry, lineMaterial);
            scene.add(line);
            
            // 创建辅助点
            const pointsCount = 10;
            const pointsGeometry = new THREE.BufferGeometry();
            const pointsArray = [];
            
            for (let i = 1; i < pointsCount; i++) {
              const t = i / pointsCount;
              const point = new THREE.Vector3().lerpVectors(start, end, t);
              pointsArray.push(point.x, point.y, point.z);
            }
            
            pointsGeometry.setAttribute('position', new THREE.Float32BufferAttribute(pointsArray, 3));
            const pointsMaterial = new THREE.PointsMaterial({
              color: 0x94a3b8,
              size: 4,
              transparent: true,
              opacity: 0.6
            });
            
            const points = new THREE.Points(pointsGeometry, pointsMaterial);
            scene.add(points);
            
            connections.push({
              line: line,
              points: points,
              start: start,
              end: end,
              pair: pair
            });
          } catch (error) {
            console.error(`连接线创建失败 ${pair[0]}->${pair[1]}: ${error.message}`);
          }
        });
      }
      
      // 创建数据包
      function createPacket() {
        if (currentPacket) {
          scene.remove(currentPacket);
        }
        
        // 创建一个简单的数据包模型 - 六边形
        const geometry = new THREE.CylinderGeometry(8, 8, 12, 6);
        const material = new THREE.MeshStandardMaterial({
          color: 0x3b82f6,
          metalness: 0.5,
          roughness: 0.3
        });
        
        currentPacket = new THREE.Mesh(geometry, material);
        currentPacket.rotation.y = Math.PI / 2;
        
        // 添加发光效果
        const glowGeometry = new THREE.SphereGeometry(12, 16, 16);
        const glowMaterial = new THREE.MeshBasicMaterial({
          color: 0x3b82f6,
          transparent: true,
          opacity: 0.5,
          side: THREE.BackSide
        });
        
        const glow = new THREE.Mesh(glowGeometry, glowMaterial);
        currentPacket.add(glow);
        
        scene.add(currentPacket);
        return currentPacket;
      }
      
      // 更新数据包位置
      function updatePacketPosition(step) {
        if (!currentPacket || !nodes[step.From] || !nodes[step.To]) return;
        
        const startPos = nodes[step.From].position.clone();
        const endPos = nodes[step.To].position.clone();
        
        // 让数据包从节点中心稍微偏移一点
        const offset = 25;
        const direction = endPos.clone().sub(startPos).normalize().multiplyScalar(offset);
        startPos.add(direction);
        
        // 终点稍微提前一点，看起来像是到达节点
        const endOffset = endPos.clone().sub(startPos).normalize().multiplyScalar(-offset);
        endPos.add(endOffset);
        
        // 计算曲线路径
        const midPoint = startPos.clone().lerp(endPos, 0.5);
        midPoint.y += 40; // 向上弯曲
        
        // 使用贝塞尔曲线计算位置
        const curve = new THREE.QuadraticBezierCurve3(
          startPos,
          midPoint,
          endPos
        );
        
        const point = curve.getPoint(packetPosition);
        currentPacket.position.copy(point);
        
        // 让数据包面向移动方向
        if (packetPosition > 0) {
          const nextPoint = curve.getPoint(Math.min(packetPosition + 0.01, 1));
          currentPacket.lookAt(nextPoint);
        }
        
        // 旋转效果
        currentPacket.rotation.z += 0.1;
      }
      
      // 创建步骤按钮
      function createStepButtons() {
        animationSteps.forEach((step, index) => {
          const button = document.createElement('button');
          button.className = 'step-btn';
          button.textContent = index + 1;
          button.addEventListener('click', () => {
            if (!isAnimating) {
              playStep(index);
            }
          });
          stepButtons.appendChild(button);
        });
      }
      
      // 播放指定步骤
      function playStep(index) {
        if (index < 0 || index >= animationSteps.length) return;
        
        currentStep = index;
        currentAnimationStep = animationSteps[index];
        packetPosition = 0;
        isAnimating = true;
        
        // 更新按钮状态
        updateStepButtons();
        
        // 更新信息面板
        updateInfoPanel();
        
        // 创建数据包
        createPacket();
      }
      
      // 更新步骤按钮状态
      function updateStepButtons() {
        const buttons = stepButtons.querySelectorAll('.step-btn');
        buttons.forEach((button, index) => {
          if (index === currentStep && isAnimating) {
            button.classList.add('step-active');
          } else {
            button.classList.remove('step-active');
          }
        });
      }
      
      // 更新信息面板
      function updateInfoPanel() {
        const noPacketEl = document.getElementById('noPacketMessage');
        const packetInfoEl = document.getElementById('packetInfoContent');
        const typeEl = document.getElementById('infoPacketType');
        const srcEl = document.getElementById('infoSrc');
        const dstEl = document.getElementById('infoDst');
        const descEl = document.getElementById('infoDescription');
        
        if (currentAnimationStep) {
          noPacketEl.classList.add('hidden');
          packetInfoEl.classList.remove('hidden');
          
          // 提取包类型
          const packetParts = currentAnimationStep.Packet.split(' ');
          const packetType = packetParts.length > 1 ? packetParts[1] : 'DATA';
          
          // 设置包类型样式
          typeEl.textContent = packetType;
          typeEl.className = 'font-bold text-xs px-2 py-1 rounded';
          
          if (packetType.includes('SYN')) {
            typeEl.classList.add('bg-blue-100', 'text-blue-800');
          } else if (packetType.includes('ACK')) {
            typeEl.classList.add('bg-green-100', 'text-green-800');
          } else {
            typeEl.classList.add('bg-gray-100', 'text-gray-800');
          }
          
          // 设置源和目的
          const srcDst = packetParts[0].split('->');
          srcEl.textContent = srcDst[0] || '';
          dstEl.textContent = srcDst[1] || '';
          
          // 设置描述
          descEl.textContent = currentAnimationStep.Remark;
          
          // 更新映射表
          updateMappingTable();
        } else {
          noPacketEl.classList.remove('hidden');
          packetInfoEl.classList.add('hidden');
        }
      }
      
      // 更新映射表
      function updateMappingTable() {
        // 清空现有内容
        mappingTableEl.innerHTML = '';
        
        // 根据当前步骤更新映射表数据
        if (currentStep >= 1) {
          // 步骤1之后添加映射数据
          mappingTable = [
            {
              key: '10.18.18.2:11111',
              target: '10.18.18.3:80',
              srcPort: '11111',
              randomPort: '22222'
            },
            {
              key: '10.18.18.2:22222',
              target: '10.18.18.3:80',
              srcPort: '11111',
              randomPort: '22222'
            }
          ];
        } else {
          mappingTable = [];
        }
        
        // 添加数据到表格
        if (mappingTable.length > 0) {
          mappingTable.forEach(item => {
            const row = document.createElement('tr');
            row.className = 'border-b border-gray-100 hover:bg-gray-50';
            
            row.innerHTML = `
              <td class="px-2 py-2 font-mono">${item.key}</td>
              <td class="px-2 py-2 font-mono">${item.target}</td>
              <td class="px-2 py-2 font-mono">${item.srcPort}</td>
              <td class="px-2 py-2 font-mono">${item.randomPort}</td>
            `;
            
            mappingTableEl.appendChild(row);
          });
        } else {
          const row = document.createElement('tr');
          row.innerHTML = `
            <td colspan="4" class="px-2 py-1 text-center text-gray-500">暂无映射数据</td>
          `;
          mappingTableEl.appendChild(row);
        }
      }
      
      // 动画循环
      function animate() {
        animationId = requestAnimationFrame(animate);
        
        // 更新节点动画
        Object.values(nodes).forEach(node => {
          const mesh = node.mesh;
          if (mesh.userData.rotationSpeed) {
            if (mesh.userData.rotationAxis === 'y') {
              mesh.rotation.y += mesh.userData.rotationSpeed;
            } else if (mesh.userData.rotationAxis === 'x') {
              mesh.rotation.x += mesh.userData.rotationSpeed;
            } else {
              mesh.rotation.z += mesh.userData.rotationSpeed;
            }
          }
          
          // 更新光环动画
          if (mesh.userData.glow) {
            const glow = mesh.userData.glow;
            glow.scale.x = 1 + Math.sin(Date.now() * 0.001) * 0.1;
            glow.scale.y = 1 + Math.sin(Date.now() * 0.001) * 0.1;
            glow.scale.z = 1 + Math.sin(Date.now() * 0.001) * 0.1;
          }
          
          // 更新环动画
          if (mesh.userData.rings) {
            mesh.userData.rings.forEach(ring => {
              ring.rotation.z += ring.userData.rotationSpeed;
            });
          }
        });
        
        // 更新数据包动画
        if (isAnimating && currentAnimationStep) {
          packetPosition += 0.01 * animationSpeed;
          
          if (packetPosition >= 1) {
            packetPosition = 1;
            isAnimating = false;
          }
          
          updatePacketPosition(currentAnimationStep);
        }
        
        // 更新控制器
        if (controls) {
          controls.update();
        }
        
        // 渲染场景
        renderer.render(scene, camera);
        if (labelRenderer) {
          labelRenderer.render(scene, camera);
        }
      }
      
      // 初始化应用
      function init() {
        initThreeJS();
        createStepButtons();
        updateInfoPanel();
      }
      
      // 启动应用
      init();
    }
</script>
<script>
var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?f0c46494f324507248dae751760ccfa3";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script>

</html>
